/*     ___ ____ ___   __   ___   ___
**    / _// __// _ | / /  / _ | / _ \    Scala classfile decoder
**  __\ \/ /__/ __ |/ /__/ __ |/ ___/    (c) 2003-2013, LAMP/EPFL
** /____/\___/_/ |_/____/_/ |_/_/        http://scala-lang.org/
**
 */

package org.json4s.scalap
package scalasig

import ClassFileParser.*
import scala.annotation.tailrec

object ScalaSigParser {
  import Main.BYTES_VALUE
  import Main.SCALA_LONG_SIG_ANNOTATION
  import Main.SCALA_SIG
  import Main.SCALA_SIG_ANNOTATION

  def scalaSigFromAnnotation(classFile: ClassFile): Option[ScalaSig] = {
    import classFile.*

    def getBytes(bytesElem: AnnotationElement): Array[Byte] = bytesElem.elementValue match {
      case ConstValueIndex(index) => bytesForIndex(index)
      case ArrayValue(signatureParts) => mergedLongSignatureBytes(signatureParts)
    }

    def mergedLongSignatureBytes(signatureParts: Seq[ElementValue]): Array[Byte] = signatureParts.flatMap {
      case ConstValueIndex(index) => bytesForIndex(index)
    }.toArray

    def bytesForIndex(index: Int) = constantWrapped(index).asInstanceOf[StringBytesPair].bytes

    classFile
      .annotation(SCALA_SIG_ANNOTATION)
      .orElse(classFile.annotation(SCALA_LONG_SIG_ANNOTATION))
      .map { case Annotation(_, elements) =>
        val bytesElem = elements.find(elem => constant(elem.elementNameIndex) == BYTES_VALUE).get
        val bytes = getBytes(bytesElem)
        val length = ByteCodecs.decode(bytes)

        ScalaSigAttributeParsers.parse(ByteCode(bytes.take(length)))
      }
  }

  def scalaSigFromAttribute(classFile: ClassFile): Option[ScalaSig] =
    classFile.attribute(SCALA_SIG).map(_.byteCode).map(ScalaSigAttributeParsers.parse)

  def parse(classFile: ClassFile): Option[ScalaSig] = {
    val scalaSig = scalaSigFromAttribute(classFile)

    scalaSig match {
      // No entries in ScalaSig attribute implies that the signature is stored in the annotation
      case Some(ScalaSig(_, _, entries)) if entries.isEmpty =>
        scalaSigFromAnnotation(classFile)
      case x => x
    }
  }

  def parse(clazz: Class[?]): Option[ScalaSig] = {
    val byteCode = ByteCode.forClass(clazz)
    val classFile = ClassFileParser.parse(byteCode)

    parse(classFile)
  }
}

object ScalaSigAttributeParsers extends ByteCodeReader {
  def parse(byteCode: ByteCode) = expect(scalaSig)(byteCode)

  val nat = apply {
    @tailrec
    def natN(in: ByteCode, x: Int): Result[ByteCode, Int, Nothing] = in.nextByte match {
      case Success(out, b) => {
        val y = (x << 7) + (b & 0x7f)
        if ((b & 0x80) == 0) Success(out, y) else natN(out, y)
      }
      case _ => Failure
    }
    in => natN(in, 0)
  }

  val rawBytes = nat >> bytes
  val entry = nat ~ rawBytes
  val symtab = nat >> entry.times
  val scalaSig = nat ~ nat ~ symtab ^~~^ ScalaSig.apply

  val utf8 = read(x => x.fromUTF8StringAndBytes.string)
  val longValue = read(_.toLong)
}

case class ScalaSig(majorVersion: Int, minorVersion: Int, table: Seq[Int ~ ByteCode]) extends DefaultMemoisable {

  case class Entry(index: Int, entryType: Int, byteCode: ByteCode) extends DefaultMemoisable {
    def scalaSig = ScalaSig.this

    def setByteCode(byteCode: ByteCode) = Entry(index, entryType, byteCode)
  }

  def hasEntry(index: Int) = table isDefinedAt index

  def getEntry(index: Int) = {
    val entryType ~ byteCode = table(index)
    Entry(index, entryType, byteCode)
  }

  def parseEntry(index: Int) = applyRule(ScalaSigParsers.parseEntry(ScalaSigEntryParsers.entry)(index))

  implicit def applyRule[A](parser: ScalaSigParsers.Parser[A]): A = ScalaSigParsers.expect(parser)(this)

  override def toString = "ScalaSig version " + majorVersion + "." + minorVersion + {
    for (i <- 0 until table.size) yield s"${i}:\t${parseEntry(i)}" // + "\n\t" + getEntry(i)
  }.mkString("\n", "\n", "")

  lazy val symbols: Seq[Symbol] = ScalaSigParsers.symbols

  lazy val topLevelClasses: List[ClassSymbol] = ScalaSigParsers.topLevelClasses
  lazy val topLevelObjects: List[ObjectSymbol] = ScalaSigParsers.topLevelObjects
}

object ScalaSigParsers extends RulesWithState with MemoisableRules {
  type S = ScalaSig
  type Parser[A] = Rule[A, String]

  val symTab = read(_.table)
  val size = symTab ^^ (_.size)

  def entry(index: Int) = memo(("entry", index)) {
    cond(_ hasEntry index) -~ read(_ getEntry index) >-> { entry => Success(entry, entry.entryType) }
  }

  def parseEntry[A](parser: ScalaSigEntryParsers.EntryParser[A])(index: Int): Parser[A] =
    entry(index) -~ parser >> { a => entry => Success(entry.scalaSig, a) }

  def allEntries[A](f: ScalaSigEntryParsers.EntryParser[A]) = size >> { n => anyOf((0 until n) map parseEntry(f)) }

  lazy val entries = allEntries(ScalaSigEntryParsers.entry) as "entries"
  lazy val symbols = allEntries(ScalaSigEntryParsers.symbol) as "symbols"
  lazy val methods = allEntries(ScalaSigEntryParsers.methodSymbol) as "methods"
  lazy val attributes = allEntries(ScalaSigEntryParsers.attributeInfo) as "attributes"

  lazy val topLevelClasses = allEntries(ScalaSigEntryParsers.topLevelClass)
  lazy val topLevelObjects = allEntries(ScalaSigEntryParsers.topLevelObject)
}

object ScalaSigEntryParsers extends RulesWithState with MemoisableRules {
  import ScalaSigAttributeParsers.longValue
  import ScalaSigAttributeParsers.nat
  import ScalaSigAttributeParsers.utf8

  type S = ScalaSig#Entry
  type EntryParser[A] = Rule[A, String]

  implicit def byteCodeEntryParser[A](rule: ScalaSigAttributeParsers.Parser[A]): EntryParser[A] = apply { entry =>
    rule(entry.byteCode) mapOut (entry setByteCode _)
  }

  def toEntry[A](index: Int) = apply { sigEntry => ScalaSigParsers.entry(index)(sigEntry.scalaSig) }

  def parseEntry[A](parser: EntryParser[A])(index: Int) = toEntry(index) -~ parser

  implicit def entryType(code: Int): org.json4s.scalap.Rule[S, S, Int, Nothing] = key filter (_ == code)

  val index = read(_.index)
  val key = read(_.entryType)

  lazy val entry: EntryParser[Any] = symbol | typeEntry | literal | name | attributeInfo | annotInfo | children | get

  val ref = byteCodeEntryParser(nat)

  val termName = 1 -~ utf8
  val typeName = 2 -~ utf8

  val name = termName | typeName as "name"

  def refTo[A](rule: EntryParser[A]): EntryParser[A] = ref >>& parseEntry(rule)

  lazy val nameRef = refTo(name)
  lazy val symbolRef = refTo(symbol)
  lazy val typeRef = refTo(typeEntry)
  lazy val constantRef = refTo(literal)

  val symbolInfo = nameRef ~ symbolRef ~ nat ~ (symbolRef.?) ~ ref ~ get ^~~~~~^ SymbolInfo.apply

  def symHeader(key: Int): EntryParser[Any] = key -~ none | (key + 64) -~ nat

  def symbolEntry(key: Int) = symHeader(key) -~ symbolInfo

  val noSymbol = 3 -^ NoSymbol
  val typeSymbol = symbolEntry(4) ^^ TypeSymbol.apply as "typeSymbol"
  val aliasSymbol = symbolEntry(5) ^^ AliasSymbol.apply as "alias"
  val classSymbol = symbolEntry(6) ~ (ref.?) ^~^ ClassSymbol.apply as "class"
  val objectSymbol = symbolEntry(7) ^^ ObjectSymbol.apply as "object"
  val methodSymbol = symHeader(8) -~ /*(ref?) -~*/ symbolInfo ~ (ref.?) ^~^ MethodSymbol.apply as "method"
  val extRef = 9 -~ nameRef ~ (symbolRef.?) ~ get ^~~^ ExternalSymbol.apply as "extRef"
  val extModClassRef = 10 -~ nameRef ~ (symbolRef.?) ~ get ^~~^ ExternalSymbol.apply as "extModClassRef"

  lazy val symbol: EntryParser[Symbol] = oneOf(
    noSymbol,
    typeSymbol,
    aliasSymbol,
    classSymbol,
    objectSymbol,
    methodSymbol,
    extRef,
    extModClassRef
  ) as "symbol"

  val classSymRef = refTo(classSymbol)
  val attribTreeRef = ref
  val typeLevel = nat
  val typeIndex = nat

  lazy val typeEntry: EntryParser[Type] = oneOf(
    11 -^ NoType,
    12 -^ NoPrefixType,
    13 -~ symbolRef ^^ ThisType.apply,
    14 -~ typeRef ~ symbolRef ^~^ SingleType.apply,
    15 -~ constantRef ^^ ConstantType.apply,
    16 -~ typeRef ~ symbolRef ~ (typeRef.*) ^~~^ TypeRefType.apply,
    17 -~ typeRef ~ typeRef ^~^ TypeBoundsType.apply,
    18 -~ classSymRef ~ (typeRef.*) ^~^ RefinedType.apply,
    19 -~ symbolRef ~ (typeRef.*) ^~^ ClassInfoType.apply,
    20 -~ typeRef ~ (symbolRef.*) ^~^ MethodType.apply,
    21 -~ typeRef ~ (refTo(typeSymbol).+) ^~^ PolyType.apply,
    // TODO: make future safe for past by doing the same transformation as in the
    // full unpickler in case we're reading pre-2.9 classfiles
    21 -~ typeRef ^^ NullaryMethodType.apply,
    22 -~ typeRef ~ (symbolRef.*) ^~^ MethodType.apply,
    42 -~ typeRef ~ (attribTreeRef.*) ^~^ AnnotatedType.apply,
    51 -~ typeRef ~ symbolRef ~ (attribTreeRef.*) ^~~^ AnnotatedWithSelfType.apply,
    48 -~ typeRef ~ (symbolRef.*) ^~^ ExistentialType.apply
  ) as "type"

  lazy val literal: EntryParser[Any] = oneOf(
    24 -^ (()),
    25 -~ longValue ^^ (_ != 0L),
    26 -~ longValue ^^ (_.toByte),
    27 -~ longValue ^^ (_.toShort),
    28 -~ longValue ^^ (_.toChar),
    29 -~ longValue ^^ (_.toInt),
    30 -~ longValue ^^ (_.toLong),
    31 -~ longValue ^^ (l => java.lang.Float.intBitsToFloat(l.toInt)),
    32 -~ longValue ^^ (java.lang.Double.longBitsToDouble),
    33 -~ nameRef,
    34 -^ null,
    35 -~ typeRef,
    36 -~ symbolRef
  )

  lazy val attributeInfo =
    40 -~ symbolRef ~ typeRef ~ (constantRef.?) ~ ((nameRef ~ constantRef).*) ^~~~^ AttributeInfo.apply // sym_Ref info_Ref {constant_Ref} {nameRef constantRef}
  lazy val children = 41 -~ (nat.*) ^^ Children.apply // sym_Ref {sym_Ref}
  lazy val annotInfo = 43 -~ (nat.*) ^^ AnnotInfo.apply // attarg_Ref {constant_Ref attarg_Ref}

  lazy val topLevelClass = classSymbol filter isTopLevelClass
  lazy val topLevelObject = objectSymbol filter isTopLevel

  def isTopLevel(symbol: Symbol) = symbol.parent match {
    case Some(_: ExternalSymbol) => true
    case _ => false
  }
  def isTopLevelClass(symbol: Symbol) = !symbol.isModule && isTopLevel(symbol)
}

case class AttributeInfo(
  symbol: Symbol,
  typeRef: Type,
  value: Option[Any],
  values: Seq[String ~ Any]
) // sym_Ref info_Ref {constant_Ref} {nameRef constantRef}
case class Children(symbolRefs: Seq[Int]) //sym_Ref {sym_Ref}

case class AnnotInfo(refs: Seq[Int]) // attarg_Ref {constant_Ref attarg_Ref}

/**
 * *************************************************
 *                  | 49 TREE len_Nat 1 EMPTYtree
 *                  | 49 TREE len_Nat 2 PACKAGEtree type_Ref sym_Ref mods_Ref name_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 3 CLASStree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 4 MODULEtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref
 *                  | 49 TREE len_Nat 5 VALDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 6 DEFDEFtree type_Ref sym_Ref mods_Ref name_Ref numtparams_Nat {tree_Ref} numparamss_Nat {numparams_Nat {tree_Ref}} tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 7 TYPEDEFtree type_Ref sym_Ref mods_Ref name_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 8 LABELtree type_Ref sym_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 9 IMPORTtree type_Ref sym_Ref tree_Ref {name_Ref name_Ref}
 *                  | 49 TREE len_Nat 11 DOCDEFtree type_Ref sym_Ref string_Ref tree_Ref
 *                  | 49 TREE len_Nat 12 TEMPLATEtree type_Ref sym_Ref numparents_Nat {tree_Ref} tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 13 BLOCKtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 14 CASEtree type_Ref tree_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 15 SEQUENCEtree type_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 16 ALTERNATIVEtree type_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 17 STARtree type_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 18 BINDtree type_Ref sym_Ref name_Ref tree_Ref
 *                  | 49 TREE len_Nat 19 UNAPPLYtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 20 ARRAYVALUEtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 21 FUNCTIONtree type_Ref sym_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 22 ASSIGNtree type_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 23 IFtree type_Ref tree_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 24 MATCHtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 25 RETURNtree type_Ref sym_Ref tree_Ref
 *                  | 49 TREE len_Nat 26 TREtree type_Ref tree_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 27 THROWtree type_Ref tree_Ref
 *                  | 49 TREE len_Nat 28 NEWtree type_Ref tree_Ref
 *                  | 49 TREE len_Nat 29 TYPEDtree type_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 30 TYPEAPPLYtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 31 APPLYtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 32 APPLYDYNAMICtree type_Ref sym_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 33 SUPERtree type_Ref sym_Ref tree_Ref name_Ref
 *                  | 49 TREE len_Nat 34 THIStree type_Ref sym_Ref  name_Ref
 *                  | 49 TREE len_Nat 35 SELECTtree type_Ref sym_Ref tree_Ref name_Ref
 *                  | 49 TREE len_Nat 36 IDENTtree type_Ref sym_Ref name_Ref
 *                  | 49 TREE len_Nat 37 LITERALtree type_Ref constant_Ref
 *                  | 49 TREE len_Nat 38 TYPEtree type_Ref
 *                  | 49 TREE len_Nat 39 ANNOTATEDtree type_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 40 SINGLETONTYPEtree type_Ref tree_Ref
 *                  | 49 TREE len_Nat 41 SELECTFROMTYPEtree type_Ref tree_Ref name_Ref
 *                  | 49 TREE len_Nat 42 COMPOUNDTYPEtree type_Ref tree_Ref
 *                  | 49 TREE len_Nat 43 APPLIEDTYPEtree type_Ref tree_Ref {tree_Ref}
 *                  | 49 TREE len_Nat 44 TYPEBOUNDStree type_Ref tree_Ref tree_Ref
 *                  | 49 TREE len_Nat 45 EXISTENTIALTYPEtree type_Ref tree_Ref {tree_Ref}
 *                  | 50 MODIFIERS len_Nat flags_Long privateWithin_Ref
 *   SymbolInfo     = name_Ref owner_Ref flags_LongNat [privateWithin_Ref] info_Ref
 *   NameInfo       = <character sequence of length len_Nat in Utf8 format>
 *   NumInfo        = <len_Nat-byte signed number in big endian format>
 *   Ref            = Nat
 *   AnnotInfoBody  = info_Ref {annotArg_Ref} {name_Ref constAnnotArg_Ref}
 *   AnnotArg       = Tree | Constant
 *   ConstAnnotArg  = Constant | AnnotInfo | AnnotArgArray
 *
 *   len is remaining length after `len`.
 */
